/***************************************************************************
 *   oboe.core.RenderSettings						  *
 *   Copyright (C) 2011 by Felipe Manga					*
 *   lufeboma@gmail.com							*
 *									 *
 *   Permission is hereby granted, free of charge, to any person obtaining *
 *   a copy of this software and associated documentation files (the	   *
 *   "Software"), to deal in the Software without restriction, including   *
 *   without limitation the rights to use, copy, modify, merge, publish,   *
 *   distribute, sublicense, and/or sell copies of the Software, and to	*
 *   permit persons to whom the Software is furnished to do so, subject to *
 *   the following conditions:						 *
 *									 *
 *   The above copyright notice and this permission notice shall be	*
 *   included in all copies or substantial portions of the Software.	   *
 *									 *
 *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,	   *
 *   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF	*
 *   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. *
 *   IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR	 *
 *   OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, *
 *   ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR *
 *   OTHER DEALINGS IN THE SOFTWARE.					   *
 ***************************************************************************/

package oboe.core;

import flash.display.BitmapData;
import flash.display.Graphics;
import flash.display.TriangleCulling;

import oboe.controllers.SceneController;
import oboe.math.Frame;
import oboe.math.Number2D;
import oboe.math.Number3D;
import oboe.math.Rotator;

class RenderSettings
{
private static inline var tmp:Float3D=new Number3D(0,0,0);

public static inline var finalPosition:Float3D = new Number3D(0,0,0);
public var rotator:Rotator;
public var position:Float3D;
public var rotation:Float3D;
public var scale:Float3D;
public var forceZSort:Float;

public var containerShiftOut:Float2D;
public var containerShiftIn:Float2D;

public var frame:Float;
public var frameLoopStart:Int;
public var frameLoopEnd:Int;
public function setAnimation( frame:Float, start:Int, end:Int ):Void
{
	this.frame = frame;
	this.frameLoopStart = start;
	this.frameLoopEnd = end;
}

public var texture:BitmapData;
public var color:UInt;

public static inline var MODE_CACHE_ONLY:Int = 1;
public static inline var MODE_DRAW_ONLY:Int = 2;
public static inline var MODE_CACHE_DRAW:Int = 3;
public var mode:Int;
public var cacheCamera:Bool = false;

public var target:Graphics;

public static inline var CULL_BACK:String = TriangleCulling.POSITIVE;
public static inline var CULL_FRONT:String = TriangleCulling.NEGATIVE;
public static inline var CULL_NONE:String = TriangleCulling.NONE;
public var culling:String;

public static inline var PROJECT_PARALLEL:Int = 1;
public static inline var PROJECT_PERSPECTIVE:Int = 2;
public static inline var PROJECT_ISOMETRIC:Int = 3;
public var projection:Int;

public var focalLength:Float;

private var invPrecision:Float;
public var rotationGranularity:Float;
public var positionGranularity:Float;

public var debugCache:Bool;


public static inline var SORT_FORCE:Function = function():Float{ return forceZSort; };
public static inline var SORT_PARALLEL:Function = SORT_PERSPECTIVE;
public static inline var SORT_ISOMETRIC:Function = function():Float{ return -(position.x+position.z); };
public static inline var SORT_PERSPECTIVE:Function = function():Float
{
	if( SceneController._camera_position )
	{
	tmp.copy3( position ).sub3( SceneController._camera_position );
	if( SceneController._camera_rotator ) SceneController._camera_rotator.apply( tmp );
	return tmp.z;
	}
	return position.z; 
};


public function new()
{
	this.position = new Number3D(0,0,0);
	this.rotation = new Number3D(0,0,0);
	this.scale = new Number3D(1,1,1);
	this.rotator = new Rotator(0,0,0);
	
	this.forceZSort = NaN;
	
	this.containerShiftOut = new Number2D();
	this.containerShiftIn = new Number2D( Oboe.flags["stageWidth"]/2, Oboe.flags["stageHeight"]/2 );
	
	this.frame = this.frameLoopStart = this.frameLoopEnd = 0;
	this.projection = Oboe.flags["PROJECTION"];
	this.color = 0x5588AA;
	this.mode = MODE_CACHE_DRAW;
	this.culling = Oboe.flags["CULLING"];
	
	this.precision = Oboe.flags["PRECISION"];
	this.positionGranularity = 0.01;
	this.rotationGranularity = 1;
	this.setFOV(Oboe.flags["stageWidth"], Oboe.flags["FOV"] );
	this.debugCache = Oboe.flags["DEBUG_3D_CACHE"];
}

public function setFOV( containerWidth:Float, angle:Float ):Void
{
	this.focalLength = (containerWidth/2) / Math.tan((Math.PI/180)*(angle/2));
}

private function setPrecision( p:Float ):Void
{
	this.invPrecision = 1/p;
}

public var precision(getPrecision, setPrecision):Float;
 	private function getPrecision():Float
{
	return 1/this.invPrecision;
}

public function getScreenCoordinates( out:Float2D ):Void
{
	switch( this.projection )
	{
	case RenderSettings.PROJECT_PARALLEL:
		out.set( 
		this.position.x-SceneController._camera_position.x, 
		-this.position.y+SceneController._camera_position.y 
		);
		break;
	case RenderSettings.PROJECT_ISOMETRIC:
		tmp.copy3( this.position ).sub3( SceneController._camera_position );
		out.set( -tmp.x+tmp.z, -tmp.y + (tmp.x+tmp.z)*0.5 );
		break;
	case RenderSettings.PROJECT_PERSPECTIVE:
		if( SceneController._camera_position )
		{
		tmp.copy3( this.position ).sub3( SceneController._camera_position );
		}
		else
		{
		tmp.copy3( this.position );
		}
		if( SceneController._camera_rotator )
		{
		SceneController._camera_rotator.apply( tmp );
		}
		var z:Float = this.focalLength / (this.focalLength + tmp.z);
		out.x = this.containerShiftOut.x + tmp.x * z;
		out.y = this.containerShiftOut.y - tmp.y * z;
		break;
	}
}

public function getCacheKey():String
{
	var acc:String;
	tmp.copy3( this.rotation );
	if( this.cacheCamera && this.projection == PROJECT_PERSPECTIVE && SceneController._camera_rotator )
	{
	tmp.sub3( SceneController._camera_rotator );
	}
	var pi2:Float=Math.PI*2;
	tmp.x %= pi2;
	tmp.y %= pi2;
	tmp.z %= pi2;
	tmp.mul( this.invPrecision*this.rotationGranularity ).floor();
	acc = tmp.x + "|" + tmp.y + "|" + tmp.z + "|";
	if( this.projection == PROJECT_PERSPECTIVE )
	{
	tmp.copy3( this.position );
	if( this.cacheCamera ) tmp.sub3( SceneController._camera_position );
	tmp.mul( this.invPrecision * this.positionGranularity ).floor();
	acc += tmp.x + "|" + tmp.y + "|" + tmp.z;
	}
	return acc;
}
}